package com.song.demo.webmvc.servlet;

import com.song.demo.webmvc.annotation.GPController;
import com.song.demo.webmvc.annotation.GPRequestMapping;
import com.song.demo.webmvc.core.GPApplicationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//servlet只是作为mvc的一个启动入口
public class GPDispatcherServlet extends HttpServlet {

    private Logger log   = LoggerFactory.getLogger(GPDispatcherServlet.class);

    private  final String LOCATION = "contextConfigLocation";

    private List<GPHandlerMapping> handlerMappings = new ArrayList<>();

    private Map<GPHandlerMapping,GPHandlerAdapter> handlerAdapters = new HashMap<>();

    private List<GPViewSolver> viewSolvers = new ArrayList<>();

    private GPApplicationContext applicationContext;



    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       try{
            doDispatch(req,resp);
       }catch (Exception e ){
           resp.getWriter().write("500 server interval error");
           e.printStackTrace();
       }
    }
    /**
     * 处理一个请求的核心方法
     * @param req
     * @param resp
     */
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //根据用户请求的url得到handler
        GPHandlerMapping handler = getHandler(req);
        if (handler == null){
            processDispatchResult(req,resp,new GPModelAndView("404"));
            return;
        }
        GPHandlerAdapter ha = getHandlerAdapter(handler);
        //这一步只是调用方法，得到返回值
        GPModelAndView mv = ha.handle(req,resp,handler);
        //这一步才是真正的输出
        processDispatchResult(req,resp,mv);
    }

    private GPHandlerAdapter getHandlerAdapter(GPHandlerMapping handler) {
        return this.handlerAdapters.get(handler);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, GPModelAndView mv) throws Exception {

        //调用viewResolver的resolverViewName的方法
        if(mv == null){
            return;
        }
        if(this.viewSolvers.isEmpty()){
            return;
        }
        for (GPViewSolver viewSolver : this.viewSolvers) {
            GPView view  = viewSolver.resolveViewName(mv.getViewName(),null);
            if (view != null){
                view.render(mv.getModel(),req,resp);
                return;
            }
        }
    }

    private GPHandlerMapping getHandler(HttpServletRequest req) {
        if(this.handlerMappings.isEmpty()){
            return null;
        }
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath,"").replaceAll("/+","/");
        for (GPHandlerMapping handler : this.handlerMappings) {
            Matcher matcher = handler.getPattern().matcher(url);
            if (!matcher.matches()){
                continue;
            }
            return handler;

        }
        return null;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //相当于把容器初始化
        GPApplicationContext gpApplicationContext = new GPApplicationContext(config.getInitParameter(LOCATION));
        this.applicationContext = gpApplicationContext;
        initStrategies();


    }

    /**
     *
     * 九大组件
     */
    private void initStrategies() {
        //==================传说中的九大组件====================
        initMultipartResolver(applicationContext); //文件上传解析
        initLocalResolver(applicationContext);//本地化解析
        initThemeResolver(applicationContext);//主题解析

        //自己实现
        initHandlerMappings(applicationContext); //url和handler映射 策略模式
        //自己实现
        initHandlerAdapters(applicationContext);//用来匹配动态 Method参数，包含类型转换，动态赋值  适配器模式
        initHandlerException(applicationContext);//如果执行中发生错误，将交由HandlerException处理


        initRequestViewName(applicationContext);//直接将请求解析大视图名

        //自己实现
        initViewResolver(applicationContext);//通过viewResolver将逻辑视图解析成具体的视图实现   策略模式


        initFlashMapManger(applicationContext);//flash 映射管理器


    }

    private void initHandlerAdapters(GPApplicationContext applicationContext) {
        //在初始化阶段，能做的就是，将这些参数的名字或者类型按照一定的顺序保存下来
        //因为后面反射调用的时候，传的形参是一个数组
        for (GPHandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapters.put(handlerMapping,new GPHandlerAdapter());
        }

    }


    //将Controller 中配置的RequestMapping 和method进行一一对应
    private void initHandlerMappings(GPApplicationContext applicationContext) {
        //首先从容器中获取所有的实例
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        try{
            for (String beanName : beanNames) {
                //到了mvc层,对外提供的方法只有一个getBean方法
                //返回的对象不是BeanWrapper
                Object controller = applicationContext.getBean(beanName);
                //GPAopUtils.getTargetObject();
                Class<?> clazz = controller.getClass();
                if(!clazz.isAnnotationPresent(GPController.class)){
                    continue;
                }
                String baseUrl =   "";
                if (clazz.isAnnotationPresent(GPRequestMapping.class)){
                    GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
                    baseUrl = requestMapping.value();
                }
                //扫描所有的public方法
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(GPRequestMapping.class)){
                        continue;
                    }
                    GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
                    String regex = ("/" + baseUrl + requestMapping.value()).replaceAll("\\*",".*").replaceAll("/+","/");
                    Pattern pattern = Pattern.compile(regex);
                    this.handlerMappings.add(new GPHandlerMapping(pattern,controller,method));
                    log.info("Mapping:" + regex + "," + method);
                }
            }

        }catch (Exception e){
            e.printStackTrace();
        }



    }
    private void initViewResolver(GPApplicationContext applicationContext) {
        String templateRoot = applicationContext.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
        File templateRootDir = new File(templateRootPath);
        for (File file : templateRootDir.listFiles()) {
            this.viewSolvers.add(new GPViewSolver(templateRoot));
        }

    }

    private void initHandlerException(GPApplicationContext applicationContext) {
    }


    private void initFlashMapManger(GPApplicationContext applicationContext) {
    }



    private void initRequestViewName(GPApplicationContext applicationContext) {
    }

    private void initThemeResolver(GPApplicationContext applicationContext) {
    }

    private void initLocalResolver(GPApplicationContext applicationContext) {
    }

    private void initMultipartResolver(GPApplicationContext applicationContext) {
    }

}
